﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Threading;

namespace Vikipedia.HashCompute
{



      public    class Hash
        {
          //  public ArrayList Files = new ArrayList();
            private List<Byte[]> m_HashSet = new List<Byte[]>();
            //public void AddFile(string newfile)
            //{
            //    foreach (string str in this.Files)
            //    {
            //        if (str == newfile)
            //        {
            //            return;
            //        }
            //    }
            //    this.Files.Add(newfile);
            //}

             private      byte[] doFileHash(FileStream file)
            {
                byte[] buffer2;
                byte[] buffer = new byte[0x947000];
                uint num2 = 0;
                uint chunksCount = GetChunksCount((uint)file.Length);
                this.m_HashSet.Clear();
                file.Seek(0L, SeekOrigin.Begin);
                while (chunksCount > 0)
                {
                    if (chunksCount == 1)
                    {
                        buffer2 = new byte[file.Length - num2];
                        file.Read(buffer2, 0, (int)(file.Length - num2));
                        this.m_HashSet.Add(HashChunk(buffer2));
                    }
                    else
                    {
                        int num = file.Read(buffer, 0, 0x947000);
                        num2 += (uint)num;
                        this.m_HashSet.Add(HashChunk(buffer));
                        GC.Collect();
                    }
                    chunksCount--;
                }
                buffer = null;
                buffer2 = null;
                GC.Collect();
                if (this.m_HashSet.Count == 1)
                {
                    byte[] buffer3 = (byte[])this.m_HashSet[0];
                    this.m_HashSet.Clear();
                    return buffer3;
                }
                return DoHashSetHash(this.m_HashSet);
            }
            public static HashResualt DoFileHash(System.IO.FileInfo info)
            {
                try
                {
                    var objhash = new Hash();

                    FileStream file = info.OpenRead();
                    byte[] buffer = objhash.doFileHash(file);
                    var flength = file.Length;
                    file.Close();
                    file = null;
                    return new HashResualt() { Hash = buffer, length = flength };
                }
                catch
                {
                    return null;
                }
            }
            public class HashResualt
            {
                public byte[] Hash;
                public long length;
                public string ToED2k(string Filename)
                {
                    return  string.Format(
                                    @"ed2k://|file|{0}|{1}|{2}|/",
                                    Filename,
                                    length,
                                    string.Concat(Hash.Select(b => b.ToString("X2")).ToArray())
                                  );
                
                }



            }




            private static byte[] DoHashSetHash(List<Byte[]> in_HashSet)
            {
                if (in_HashSet.Count == 0)
                {
                    return null;
                }
                byte[] dst = new byte[0x10 * in_HashSet.Count];
                int num = 0;
                foreach (byte[] buffer2 in in_HashSet)
                {
                    Buffer.BlockCopy(buffer2, 0, dst, 0x10 * num, 0x10);
                    num++;
                }
                byte[] buffer3 = new byte[0x10];
                MD4 md = new MD4();
                return md.GetByteHashFromBytes(dst);
            }

            private static uint GetChunkNumberAt(uint position)
            {
                return (position / 0x947000);
            }

            private static uint GetChunksCount(uint size)
            {


                uint num = ((size % 0x947000) > uint.MinValue) ? Convert.ToUInt32(1) : uint.MinValue;
                return (num + (size / 0x947000));
            }

            private static uint GetCrumbsCount(uint size)
            {
                uint num = ((size % 0x76c00) > uint.MinValue) ? Convert.ToUInt32(1) : uint.MinValue;
                return (num + (size / 0x76c00));
            }

            private static byte[] HashChunk(byte[] chunk)
            {
                MD4 md = new MD4();
                return md.GetByteHashFromBytes(chunk);
            }

            private static List<Byte[]> HashCrumbsInChunk(byte[] chunk)
            {
                int num1 = chunk.Length / 0x76c00;
                byte[] dst = new byte[0x76c00];
                uint num = 0;
                uint num2 = 0;
                MD4 md = new MD4();
                List<Byte[]> list = new List<Byte[]>();
                while (num2 < (chunk.Length - 1))
                {
                    num2 = (num + 0x76c00) - 1;
                    if (num2 >= chunk.Length)
                    {
                        num2 = (uint)(chunk.Length - 1);
                        dst = new byte[(num2 - num) + 1];
                    }
                    Buffer.BlockCopy(chunk, (int)num, dst, 0, ((int)(num2 - num)) + 1);
                    byte[] byteHashFromBytes = md.GetByteHashFromBytes(dst);
                    byte[] buffer2 = new byte[8];
                    Buffer.BlockCopy(byteHashFromBytes, 0, buffer2, 0, 8);
                    list.Add(buffer2);
                    num = num2 + 1;
                }
                return list;
            }



            //private void m_ExecuteThread()
            //{
            //    this.m_Hashing = true;
            //    foreach (string str in this.Files)
            //    {
            //        byte[] hash = this.DoFileHash(str);
            //      //  CLog.Log(Constants.Log.Notify, "HASH_END", Path.GetFileName(str));
            //        if (hash != null)
            //        {
            //      //      CKernel.FilesList.AddFile(str, hash, this.m_HashSet);
            //        }
            //    }
            //    this.Files.Clear();
            //    this.m_Hashing = false;
            ////    CKernel.FilesList.LoadEnded();
            //}

            //public bool Hashing
            //{
            //    get
            //    {
            //        return this.m_Hashing;
            //    }
            //}

            //public ArrayList HashSet
            //{
            //    get
            //    {
            //        return this.m_HashSet;
            //    }
            //}

            private class MD4
            {
                private const int BLOCK_LENGTH = 0x40;
                private byte[] buffer;
                private uint[] context;
                private long count;
                private uint[] X;

                public MD4()
                {
                    this.context = new uint[4];
                    this.buffer = new byte[0x40];
                    this.X = new uint[0x10];
                    this.engineReset();
                }

                private MD4(Hash.MD4 md)
                    : this()
                {
                    this.context = (uint[])md.context.Clone();
                    this.buffer = (byte[])md.buffer.Clone();
                    this.count = md.count;
                }

                private static string bytesToHex(byte[] a, int len)
                {
                    string str = BitConverter.ToString(a);
                    StringBuilder builder = new StringBuilder((len - 2) / 2);
                    for (int i = 0; i < str.Length; i++)
                    {
                        if (str[i] != '-')
                        {
                            builder.Append(str[i]);
                        }
                    }
                    return builder.ToString();
                }

                public object Clone()
                {
                    return new Hash.MD4(this);
                }

                private byte[] engineDigest()
                {
                    int num = (int)(this.count % 0x40L);
                    int num2 = (num < 0x38) ? (0x38 - num) : (120 - num);
                    byte[] input = new byte[num2 + 8];
                    input[0] = 0x80;
                    for (int i = 0; i < 8; i++)
                    {
                        input[num2 + i] = (byte)((this.count * 8L) >> (8 * i));
                    }
                    this.engineUpdate(input, 0, input.Length);
                    byte[] buffer2 = new byte[0x10];
                    for (int j = 0; j < 4; j++)
                    {
                        for (int k = 0; k < 4; k++)
                        {
                            buffer2[(j * 4) + k] = (byte)(this.context[j] >> (8 * k));
                        }
                    }
                    this.engineReset();
                    return buffer2;
                }

                private void engineReset()
                {
                    this.context[0] = 0x67452301;
                    this.context[1] = 0xefcdab89;
                    this.context[2] = 0x98badcfe;
                    this.context[3] = 0x10325476;
                    this.count = 0L;
                    for (int i = 0; i < 0x40; i++)
                    {
                        this.buffer[i] = 0;
                    }
                }

                private void engineUpdate(byte b)
                {
                    int index = (int)(this.count % 0x40L);
                    this.count += 1L;
                    this.buffer[index] = b;
                    if (index == 0x3f)
                    {
                        this.transform(ref this.buffer, 0);
                    }
                }

                private void engineUpdate(byte[] input, int offset, int len)
                {
                    if (((offset < 0) || (len < 0)) || ((offset + len) > input.Length))
                    {
                        throw new ArgumentOutOfRangeException();
                    }
                    int destinationIndex = (int)(this.count % 0x40L);
                    this.count += len;
                    int length = 0x40 - destinationIndex;
                    int num3 = 0;
                    if (len >= length)
                    {
                        Array.Copy(input, offset + num3, this.buffer, destinationIndex, length);
                        this.transform(ref this.buffer, 0);
                        num3 = length;
                        while (((num3 + 0x40) - 1) < len)
                        {
                            this.transform(ref input, offset + num3);
                            num3 += 0x40;
                        }
                        destinationIndex = 0;
                    }
                    if (num3 < len)
                    {
                        Array.Copy(input, offset + num3, this.buffer, destinationIndex, len - num3);
                    }
                }

                private uint FF(uint a, uint b, uint c, uint d, uint x, int s)
                {
                    uint num = (a + ((b & c) | (~b & d))) + x;
                    return ((num << s) | (num >> (0x20 - s)));
                }

                public byte[] GetByteHashFromByte(byte b)
                {
                    Hash.MD4 md = new Hash.MD4();
                    md.engineUpdate(b);
                    return md.engineDigest();
                }

                public byte[] GetByteHashFromBytes(byte[] b)
                {
                    Hash.MD4 md = new Hash.MD4();
                    md.engineUpdate(b, 0, b.Length);
                    return md.engineDigest();
                }

                public byte[] GetByteHashFromString(string s)
                {
                    byte[] bytes = Encoding.UTF8.GetBytes(s);
                    Hash.MD4 md = new Hash.MD4();
                    md.engineUpdate(bytes, 0, bytes.Length);
                    return md.engineDigest();
                }

                public string GetHexHashFromByte(byte b)
                {
                    byte[] byteHashFromByte = this.GetByteHashFromByte(b);
                    return bytesToHex(byteHashFromByte, byteHashFromByte.Length);
                }

                public string GetHexHashFromBytes(byte[] b)
                {
                    byte[] byteHashFromBytes = this.GetByteHashFromBytes(b);
                    return bytesToHex(byteHashFromBytes, byteHashFromBytes.Length);
                }

                public string GetHexHashFromString(string s)
                {
                    byte[] byteHashFromString = this.GetByteHashFromString(s);
                    return bytesToHex(byteHashFromString, byteHashFromString.Length);
                }

                private uint GG(uint a, uint b, uint c, uint d, uint x, int s)
                {
                    uint num = ((a + ((b & (c | d)) | (c & d))) + x) + 0x5a827999;
                    return ((num << s) | (num >> (0x20 - s)));
                }

                private uint HH(uint a, uint b, uint c, uint d, uint x, int s)
                {
                    uint num = ((a + ((b ^ c) ^ d)) + x) + 0x6ed9eba1;
                    return ((num << s) | (num >> (0x20 - s)));
                }

                private void transform(ref byte[] block, int offset)
                {
                    for (int i = 0; i < 0x10; i++)
                    {
                        this.X[i] = (uint)((((block[offset++] & 0xff) | ((block[offset++] & 0xff) << 8)) | ((block[offset++] & 0xff) << 0x10)) | ((block[offset++] & 0xff) << 0x18));
                    }
                    uint a = this.context[0];
                    uint b = this.context[1];
                    uint c = this.context[2];
                    uint d = this.context[3];
                    a = this.FF(a, b, c, d, this.X[0], 3);
                    d = this.FF(d, a, b, c, this.X[1], 7);
                    c = this.FF(c, d, a, b, this.X[2], 11);
                    b = this.FF(b, c, d, a, this.X[3], 0x13);
                    a = this.FF(a, b, c, d, this.X[4], 3);
                    d = this.FF(d, a, b, c, this.X[5], 7);
                    c = this.FF(c, d, a, b, this.X[6], 11);
                    b = this.FF(b, c, d, a, this.X[7], 0x13);
                    a = this.FF(a, b, c, d, this.X[8], 3);
                    d = this.FF(d, a, b, c, this.X[9], 7);
                    c = this.FF(c, d, a, b, this.X[10], 11);
                    b = this.FF(b, c, d, a, this.X[11], 0x13);
                    a = this.FF(a, b, c, d, this.X[12], 3);
                    d = this.FF(d, a, b, c, this.X[13], 7);
                    c = this.FF(c, d, a, b, this.X[14], 11);
                    b = this.FF(b, c, d, a, this.X[15], 0x13);
                    a = this.GG(a, b, c, d, this.X[0], 3);
                    d = this.GG(d, a, b, c, this.X[4], 5);
                    c = this.GG(c, d, a, b, this.X[8], 9);
                    b = this.GG(b, c, d, a, this.X[12], 13);
                    a = this.GG(a, b, c, d, this.X[1], 3);
                    d = this.GG(d, a, b, c, this.X[5], 5);
                    c = this.GG(c, d, a, b, this.X[9], 9);
                    b = this.GG(b, c, d, a, this.X[13], 13);
                    a = this.GG(a, b, c, d, this.X[2], 3);
                    d = this.GG(d, a, b, c, this.X[6], 5);
                    c = this.GG(c, d, a, b, this.X[10], 9);
                    b = this.GG(b, c, d, a, this.X[14], 13);
                    a = this.GG(a, b, c, d, this.X[3], 3);
                    d = this.GG(d, a, b, c, this.X[7], 5);
                    c = this.GG(c, d, a, b, this.X[11], 9);
                    b = this.GG(b, c, d, a, this.X[15], 13);
                    a = this.HH(a, b, c, d, this.X[0], 3);
                    d = this.HH(d, a, b, c, this.X[8], 9);
                    c = this.HH(c, d, a, b, this.X[4], 11);
                    b = this.HH(b, c, d, a, this.X[12], 15);
                    a = this.HH(a, b, c, d, this.X[2], 3);
                    d = this.HH(d, a, b, c, this.X[10], 9);
                    c = this.HH(c, d, a, b, this.X[6], 11);
                    b = this.HH(b, c, d, a, this.X[14], 15);
                    a = this.HH(a, b, c, d, this.X[1], 3);
                    d = this.HH(d, a, b, c, this.X[9], 9);
                    c = this.HH(c, d, a, b, this.X[5], 11);
                    b = this.HH(b, c, d, a, this.X[13], 15);
                    a = this.HH(a, b, c, d, this.X[3], 3);
                    d = this.HH(d, a, b, c, this.X[11], 9);
                    c = this.HH(c, d, a, b, this.X[7], 11);
                    b = this.HH(b, c, d, a, this.X[15], 15);
                    this.context[0] += a;
                    this.context[1] += b;
                    this.context[2] += c;
                    this.context[3] += d;
                }
            }
        }
   

}
